package ro.pub.cs.stadium.document.logic;

import java.util.Map;
import java.util.Vector;


public class Expression implements IExpression {

	String condition;
	VariableList variables;
	Node root;
	
	public static final int AND = 0;
	public static final int OR = 1;
	public static final int NOT = 2;
	public static final int VAR = -1;
	public static final int NO_OP = -1;

	public Expression(String condition, VariableList variables) throws ExpressionException{
		this.condition = condition;
		this.variables = variables;
		root = getParseTree(condition);
	}
	
	public Expression(String conditie) throws ExpressionException{
		this(conditie,new VariableList());
	}
	
	public boolean evaluate(Map<Variable, Boolean> Values) throws ExpressionException{
		return evaluate(root,Values);
	}
	
	private boolean evaluate(Node n, Map<Variable, Boolean> Values) {
		if (n.type==VAR)
			return Values.get(n.var).booleanValue();
		if (n.type==AND)
			return evaluate(n.left,Values) && evaluate(n.right,Values);
		if (n.type==OR)
			return evaluate(n.left,Values) || evaluate(n.right,Values);
		if (n.type==NOT)
			return !evaluate(n.left,Values);
		return false;
	}

	
	private Node getParseTree(String cond) throws ExpressionException {
		Vector<String> v = parse(cond);
		if (v.size()==1) {
			Variable var = new Variable(v.firstElement());
			var.setType(false);
			variables.addVariable(var);
			return new Node(var);
		}
		
		if (v.size()==2) {
			if (!v.get(0).trim().equals("!")) throw new ExpressionException("Unknown token:"+v.get(0));
			return new Node(NOT, getParseTree(v.get(1)),null);
		}
		
		if (v.size()==3) {
			int type = NO_OP;
			switch(v.get(1).trim().charAt(0)) {
				case '&': type = AND; break;
				case '|': type = OR; break;
				default: throw new ExpressionException("Unknown operator:"+v.get(1).trim().charAt(0));
			}
			return new Node(type,getParseTree(v.get(0)),getParseTree(v.get(2)));
		}
		throw new ExpressionException("Too many tokens...use parantheses");
	}
	
public String toString() {
	return root.toString();
}

public VariableList getVariables() {
	return variables;
}

private Vector<String> parse(String exp) {
	exp.trim();
	int d=0;
	int[] depth = new int[exp.length()];
	for(int i=0;i<exp.length();i++) {
		switch(exp.charAt(i)) {
			case '(':
				d++;
				depth[i]=d;
				break;
			case ')':
				depth[i]=d;
				d--;
				break;
			default: 
				depth[i]=d;
				break;
		}
	}
	
	int min=depth[0];
	for(int i = 0; i < exp.length(); i++)
		if (depth[i]<min)
			min = depth[i];
	if(min>0)
		return parse(exp.substring(1,exp.length()-1));
	
	Vector<String> tokens = new Vector<String>();
	int i=0;
	int state = 0;
	if (depth[0]==0)
		state = 0;
	else
		state =1;
	int start=0;
	
	while(i<exp.length()) {
		switch(state) {
		case 0:
			if (exp.charAt(i) == '&' || exp.charAt(i) == '|' || exp.charAt(i) == '!' ) {
				if (start < i)
					tokens.add(exp.substring(start,i));
				tokens.add(exp.substring(i,i+1));
				i++;
				start = i;
				break;
			} 
			if (depth[i]>0) {
				state=1;
				if (start < i)
					tokens.add(exp.substring(start,i));
				start = i;
			}
			else
				i++;
			break;
		case 1:
			if (depth[i]==0) {
				state=0;
				if (start < i)
					tokens.add(exp.substring(start,i));
				start = i;
			}
			else
				i++;
			break;
		}
	}
	tokens.add(exp.substring(start,i));
	return tokens;
}
	
	
class Node {
	public Node left = null,right = null;
	public Variable var = null;
	public int type;

	Node(int type, Node left, Node right) {
		this.type = type;
		this.left = left;
		this.right = right;
	}
	
	Node(Variable var) {
		this.type = Expression.VAR;
		this.var = var;
	}
	
	public String toString() {
		if (type == Expression.VAR) return var.getName();
		String L="",R="";
		if (left!=null) {
			L = left.toString();
			if (left.type!=Expression.VAR && left.type!=Expression.NOT) L = "(" + L + ")";
		}
		
		if (right!=null) {
			R = right.toString();
			if (right.type!=Expression.VAR && right.type!=Expression.NOT) R = "(" + R + ")";
		}
			
		switch(type) {
		case Expression.NOT: return "!"+L;
		case Expression.AND: return L+"&"+R;
		case Expression.OR: return L+"|"+R;
			}
			return null;
		}
	}
}
